/**
 * updated 2022/04/12
 */
class DateUtils {
  /**
   * 解析日期
   * @param {Date|String} param 格式如：2022-01-01 10:00:00 或 2022-01-01
   */
  parseDate(date) {
    if (typeof date == 'string') {
      // ISO 8601
      const iosTimeStr = date.replace(' ', 'T')
      date = new Date(iosTimeStr)
    }
    return date
  }

  /**
   *
   * @param {Date|String} date
   * @param {boolean} onlyDate 只保留日期
   * @returns
   */
  getDisplayStr(date) {
    return this.getDateTimeStr(date, false)
  }

  getDateStr(date) {
    return this.getDateTimeStr(date, true)
  }

  /**
   * 获取时间字符串 例如 8:00,14:05
   * @param {String|Date} date
   * @returns
   */
  getTimeStr(date) {
    date = this.parseDate(date)
    const minutes = date.getMinutes() > 9 ? date.getMinutes() : '0' + date.getMinutes()
    return `${date.getHours()}:${minutes}`
  }

  /**
   * 获取周中的"天"
   * @param {Date|String} date
   */
  getDay(date) {
    date = this.parseDate(date)
    return date.getDay(date)
  }

  /**
   * 获取今天的日期
   */
  getToday() {
    return this.getDateByOffset(0)
  }

  /**
   * 获取昨天的日期
   */
  getYesterday() {
    return this.getDateByOffset(-1)
  }

  /**
   * 获取距离今天 n 天的日期
   *
   * @param {Number}} offset 【0:今天，1：明天，2：后天，-1：昨天】
   */
  getDateByOffset(offset) {
    const today = new Date()
    const date = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()))
    date.setDate(date.getDate() + offset)
    return date
  }

  /**
   * 获取本月的最大 date
   * @param {String|Date} date
   * @returns 本月最大日期
   */
  getLastDateOfMonth(date) {
    date = this.parseDate(date)
    return new Date(date.getFullYear(), date.getMonth() + 1, 0)
  }

  /**
   * 获取本月第一天的日期
   * @param {String|Date} date
   * @returns 本月第一天
   */
  getFirstDateOfMonth(date) {
    date = this.parseDate(date)

    return new Date(date.getFullYear(), date.getMonth(), 1)
  }

  /**
   * 获取周中的”天“，
   * @param {Date|String} date
   * @return 中文格式的天数
   */
  getCnDay(date) {
    const day = this.getDay(date)
    return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][day]
  }

  /**
   * 获取日期所在周的周一（周一为每周的第一天）
   * @param {Date} date
   * @returns monday
   */
  getMonday4Date(date) {
    const day = this.getDay(date)
    const dayOffset = (day + 6) % 7
    const newDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
    newDate.setDate(newDate.getDate() - dayOffset)
    return newDate
  }

  /**
   * 获取此日期所在周的周日（周一为每周的第一天）
   * @param {Date} date
   */
  getSunday4Date(date) {
    const day = date.getDay()
    const dayOffset = 7 - day
    const nextSunday = new Date(date.getTime())
    nextSunday.setDate(date.getDate() + dayOffset)
    return nextSunday
  }

  /**
   * 获取一年中的所有周
   * @param {Number} year
   * @returns {Array} 周列表 {monday:$date,sunday:$date}
   */
  getAllWeeksOfYear(year) {
    const weeks = []
    const iterator = getDateIterator(year)

    while (iterator.hasNext()) {
      const nextDate = iterator.next()
      if (nextDate.getDay() == 1) {
        const curWeek = { monday: nextDate, sunday: this.getSunday4Date(nextDate) }
        weeks.push(curWeek)
      }
    }

    return weeks

    /**
     * 获取日期遍历器
     * @param {Number} year 年份
     * @returns
     */
    function getDateIterator(year) {
      let curDate = null

      return {
        hasNext() {
          if (curDate == null) {
            return true
          }
          const nextDate = getNextDate(curDate)
          return nextDate.getFullYear() == year
        },
        next() {
          if (curDate == null) {
            curDate = new Date(Date.UTC(year, 0, 1))
          } else {
            curDate = getNextDate(curDate)
          }

          return curDate
        }
      }

      /**
       * 获取第二天的日期
       * @param {Date} date 今天的日期
       * @returns
       */
      function getNextDate(date) {
        const newDate = new Date(date.getTime())
        newDate.setDate(date.getDate() + 1)
        return newDate
      }
    }
  }

  /**
   * 获取当前日期所在周数（周一为一周的开始）
   * @param {Number} year 年
   * @param {Date} date 日期
   * @returns -1:未找到 0:第一周
   */
  getWeekNum(year, date) {
    const weeks = this.getAllWeeksOfYear(year)
    for (let i = -1; ++i < weeks.length; ) {
      const curWeek = weeks[i]

      if (this.between(date, curWeek.monday, curWeek.sunday)) {
        return i
      }
    }
    return -1
  }

  /**
   * 获取当月第一天的日期
   */
  getThisMonth() {
    const today = this.getToday()
    return new Date(today.getFullYear(), today.getMonth(), 1)
  }

  /**
   * 获取可直接显示的日期字符串
   * @param {String|Date} date 日期对象
   * @returns
   */
  getDateTimeStr(date) {
    if (!date) {
      return ''
    }
    if (typeof date == 'string') {
      date = this.parseDate(date)
    }

    const timestampOffset = Date.prototype.getTimezoneOffset.call(date) * 60 * 1000
    const nowLocalTimestamp = Date.now() - timestampOffset
    const nowTimePortion = nowLocalTimestamp % (24 * 3600 * 1000)
    const nowDatePortion = nowLocalTimestamp - nowTimePortion

    const localTimestamp = date.getTime() - timestampOffset
    var timePortion = localTimestamp % (3600 * 1000 * 24)
    var datePortion = localTimestamp - timePortion

    // 使用当地时间戳进行比较，使用标准时间戳会出问题
    const dateTimestampDiff = nowDatePortion - datePortion
    let dayDiff = Math.floor(dateTimestampDiff / (24 * 3600 * 1000))

    let timePortionStr =
      (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) +
      ':' +
      (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())

    switch (dayDiff) {
      case 0:
        return '今天 ' + timePortionStr
      case 1:
        return '昨天 ' + timePortionStr
      case 2:
        return '前天 ' + timePortionStr
      case -1:
        return '明天 ' + timePortionStr
      case -2:
        return '后天 ' + timePortionStr
      default: {
        return this.formatDate(date, 'yyyy-MM-dd')
      }
    }
  }

  /**
   * 获取日期天的差值
   * date1 - date2
   * @param {*} date1
   * @param {*} date2
   * @returns
   */
  getDayDiff(date1, date2) {
    if (typeof date1 == 'string') {
      date1 = this.parseDate(date1)
    }
    if (typeof date2 == 'string') {
      date2 = this.parseDate(date2)
    }

    const timestampOffset = Date.prototype.getTimezoneOffset.call(date1) * 60 * 1000

    const date1LocalTimestamp = date1.getTime() - timestampOffset
    const date1TimePortion = date1LocalTimestamp % (24 * 3600 * 1000)
    const date1DatePortion = date1LocalTimestamp - date1TimePortion

    const date2Timestamp = date2.getTime() - timestampOffset
    var date2TimePortion = date2Timestamp % (3600 * 1000 * 24)
    var date2DatePortion = date2Timestamp - date2TimePortion

    // 使用当地时间戳进行比较，使用标准时间戳会出问题
    const dateTimestampDiff = date1DatePortion - date2DatePortion
    return Math.floor(dateTimestampDiff / (24 * 3600 * 1000))
  }

  /**
   * 格式化时间
   * @param {String|Date} date
   * @param {String} fmt
   * @returns
   */
  formatDate(date, fmt) {
    if (null == date || undefined == date) return ''
    if (typeof date == 'string') {
      date = this.parseDate(date)
    }
    var o = {
      'M+': date.getMonth() + 1, //月份
      'd+': date.getDate(), //日
      'H+': date.getHours(), //小时
      'm+': date.getMinutes(), //分
      's+': date.getSeconds(), //秒
      S: date.getMilliseconds() //毫秒
    }
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
    for (var k in o) {
      if (new RegExp('(' + k + ')').test(fmt)) {
        fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
      }
    }

    return fmt
  }

  /**
   * 当前日期是否为今天
   * @param {String|Date} date
   */
  isToday(date) {
    date = this.parseDate(date)
    const today = new Date()
    return this.isSameDate(today, date)
  }

  /**
   * 日期是否为今年的日期
   * @param {*} date
   * @returns
   */
  isThisYear(date) {
    date = this.parseDate(date)
    return date.getFullYear() == this.getToday().getFullYear()
  }

  /**
   * 判断两个日是否为同一天
   *
   * @param {String | Date} date1 日期 1
   * @param {String | Date} date2 日期 2
   * @returns 两日期是否为同一天
   */
  isSameDate(date1, date2) {
    if (!date1 || !date2) {
      return false
    }
    date1 = this.parseDate(date1)
    date2 = this.parseDate(date2)
    return (
      date1.getFullYear() == date2.getFullYear() &&
      date1.getMonth() == date2.getMonth() &&
      date1.getDate() == date2.getDate()
    )
  }

  /**
   * 判断两个时间范围是否相同
   *
   * @param {Array} dateRange1
   * @param {Array} dateRange2
   * @returns
   */
  isSameDateRange(dateRange1, dateRange2) {
    if (!dateRange1 || !dateRange2) {
      return false
    }
    if (dateRange1.length != dateRange2.length) {
      return false
    }
    if (dateRange1.length != 2) {
      return false
    }
    if (!this.isSameDate(dateRange1[0], dateRange2[0])) {
      return false
    }

    if (!this.isSameDate(dateRange1[1], dateRange2[1])) {
      return false
    }

    return true
  }

  /**
   * 判断日期是否在某一个范围内
   * @param {Date} date 需要判断的日期
   * @param {Date} startDate 开始日期
   * @param {Date} endDate 结束日期
   */
  between(date, startDate, endDate) {
    const timestamp = date.getTime()
    return timestamp >= startDate.getTime() && timestamp <= endDate.getTime()
  }
}

export default new DateUtils()
